package com.zerui.manager.access.auth.task.gateway.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zerui.manager.access.auth.task.domain.AuthTask;
import com.zerui.manager.access.auth.task.domain.AuthTaskDetail;
import com.zerui.manager.access.auth.task.entity.AccessAuthTaskDO;
import com.zerui.manager.access.auth.task.entity.AccessAuthTaskDetailDO;
import com.zerui.manager.access.auth.task.gateway.AuthTaskGateway;
import com.zerui.manager.access.auth.task.mapper.AccessAuthTaskDetailMapper;
import com.zerui.manager.access.auth.task.mapper.AccessAuthTaskMapper;
import com.zerui.manager.web.enums.AuthTaskSyncStatusEnum;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class AuthTaskGatewayImpl implements AuthTaskGateway {

    @Resource
    private AccessAuthTaskMapper accessAuthTaskMapper;
    @Resource
    private AccessAuthTaskDetailMapper accessAuthTaskDetailMapper;

    /**
     * 查询指定任务ID的任务信息
     *
     * @param taskId
     * @return
     */
    @Override
    public AuthTask selectByTaskId(Long taskId) {
        AccessAuthTaskDO accessAuthTaskDO = accessAuthTaskMapper.selectById(taskId);
        AuthTask authTask = null;
        if (null != accessAuthTaskDO) {
            authTask = accessAuthTaskDO.toAuthTask();
            List<AuthTaskDetail> taskDetails = accessAuthTaskDetailMapper.selectListByTaskId(taskId);
            authTask.setDetails(taskDetails);
        }
        return authTask;
    }

    /**
     * 此处pyzhang将该方法返回值从 Boolean 值改成 返回授权任务的主键
     *
     * @param authTask
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveAuthTask(AuthTask authTask) {
        AccessAuthTaskDO task = AccessAuthTaskDO.of(authTask);
        this.accessAuthTaskMapper.insert(task);

        List<AccessAuthTaskDetailDO> details = AccessAuthTaskDetailDO.ofBatch(task.getId(), authTask.getDetails());
        Collections.shuffle(details);
        for (AccessAuthTaskDetailDO detail : details) {
            this.accessAuthTaskDetailMapper.insert(detail);
        }
        return task.getId();
    }

    @Override
    public List<AuthTaskDetail> getUnSyncTasks() {
        LambdaQueryWrapper<AccessAuthTaskDetailDO> query = Wrappers.lambdaQuery(AccessAuthTaskDetailDO.class);
        query.eq(AccessAuthTaskDetailDO::getSyncStatus, 0);
        query.le(AccessAuthTaskDetailDO::getRetryTimes, 5); // 重试次数小于 5 次的
        query.orderByAsc(AccessAuthTaskDetailDO::getUpdateTime);
        query.last("limit 20");
        List<AccessAuthTaskDetailDO> details = this.accessAuthTaskDetailMapper.selectList(query);
        if (CollectionUtils.isEmpty(details)) {
            return Collections.emptyList();
        }
        return details.stream().map(AccessAuthTaskDetailDO::toDomain).collect(Collectors.toList());
    }

    @Override
    public List<AuthTaskDetail> getTaskDetailsByTaskId(Long taskId) {
        return this.accessAuthTaskDetailMapper.selectListByTaskId(taskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean syncSuccess(AuthTaskDetail detail) {
        // 更新执行状态
        AccessAuthTaskDetailDO detailDO = new AccessAuthTaskDetailDO();
        detailDO.setId(detail.getId());
        detailDO.setSyncStatus(AuthTaskSyncStatusEnum.SYNC_DONE.getCode());
        detailDO.setUpdateTime(LocalDateTime.now());
        detailDO.setResponseData(detail.getResponseData());
        this.accessAuthTaskDetailMapper.updateById(detailDO);

        finishTask(detail);
        return true;
    }

    private void finishTask(AuthTaskDetail detail) {
        // 查询任务是否全部执行完毕
        LambdaUpdateWrapper<AccessAuthTaskDetailDO> countQuery = Wrappers.lambdaUpdate(AccessAuthTaskDetailDO.class);
        countQuery.eq(AccessAuthTaskDetailDO::getTaskId, detail.getTaskId());
        countQuery.in(AccessAuthTaskDetailDO::getSyncStatus, 0, 1);
        Integer unSyncCount = this.accessAuthTaskDetailMapper.selectCount(countQuery);
        if (unSyncCount == 0) {
            // 全部执行完毕
            AccessAuthTaskDO taskDO = new AccessAuthTaskDO();
            taskDO.setId(detail.getTaskId());
            taskDO.setSyncStatus(AuthTaskSyncStatusEnum.SYNC_DONE.getCode());
            taskDO.setUpdateTime(LocalDateTime.now());
            this.accessAuthTaskMapper.updateById(taskDO);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean syncFailure(AuthTaskDetail detail) {
        // 更新执行状态
        if (detail.getRetryTimes() < 5) {
            // 等待下次重试
            AccessAuthTaskDetailDO detailDO = new AccessAuthTaskDetailDO();
            detailDO.setId(detail.getId());
            detailDO.setSyncStatus(0);
            detailDO.setUpdateTime(LocalDateTime.now());
            detailDO.setResponseData(detail.getResponseData());
            this.accessAuthTaskDetailMapper.updateById(detailDO);
            return true;
        }
        // 等待下次重试
        AccessAuthTaskDetailDO detailDO = new AccessAuthTaskDetailDO();
        detailDO.setId(detail.getId());
        detailDO.setSyncStatus(AuthTaskSyncStatusEnum.SYNC_FAILURE.getCode());
        detailDO.setUpdateTime(LocalDateTime.now());
        detailDO.setResponseData(detail.getResponseData());
        this.accessAuthTaskDetailMapper.updateById(detailDO);

        finishTask(detail);
        return true;
    }

    @Override
    public boolean syncIng(AuthTaskDetail detail) {
        LambdaUpdateWrapper<AccessAuthTaskDetailDO> updateQuery = Wrappers.lambdaUpdate(AccessAuthTaskDetailDO.class);
        updateQuery.set(AccessAuthTaskDetailDO::getSyncStatus, AuthTaskSyncStatusEnum.SYNC_DOING.getCode());
        updateQuery.set(AccessAuthTaskDetailDO::getRequestId, detail.getRequestId());
        updateQuery.eq(AccessAuthTaskDetailDO::getId, detail.getId());
        this.accessAuthTaskDetailMapper.update(null, updateQuery);
        return true;
    }

    @Override
    public void syncTaskItemStatus(Long taskItemId, Integer syncStatus, LocalDateTime date, Integer retryTimes, String requestId) {
        LambdaUpdateWrapper<AccessAuthTaskDetailDO> updateQuery = Wrappers.lambdaUpdate(AccessAuthTaskDetailDO.class);
        updateQuery.set(AccessAuthTaskDetailDO::getSyncStatus, syncStatus);
        updateQuery.set(AccessAuthTaskDetailDO::getUpdateTime, date);
        updateQuery.set(retryTimes != null, AccessAuthTaskDetailDO::getRetryTimes, retryTimes);
        updateQuery.set(StringUtils.isNotBlank(requestId), AccessAuthTaskDetailDO::getRequestId, requestId);
        updateQuery.eq(AccessAuthTaskDetailDO::getId, taskItemId);
        this.accessAuthTaskDetailMapper.update(null, updateQuery);
    }

    @Override
    public AuthTaskDetail getDetailByRequestId(String requestId) {
        LambdaQueryWrapper<AccessAuthTaskDetailDO> query = Wrappers.lambdaQuery(AccessAuthTaskDetailDO.class);
        query.eq(AccessAuthTaskDetailDO::getRequestId, requestId);
        List<AccessAuthTaskDetailDO> list = this.accessAuthTaskDetailMapper.selectList(query);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        return list.get(0).toDomain();
    }

    @Override
    public void syncTaskStatus(Long taskId, Integer syncStatus, LocalDateTime date) {
        LambdaUpdateWrapper<AccessAuthTaskDO> updateQuery = Wrappers.lambdaUpdate(AccessAuthTaskDO.class);
        updateQuery.set(AccessAuthTaskDO::getSyncStatus, syncStatus);
        updateQuery.set(AccessAuthTaskDO::getUpdateTime, date);
        updateQuery.eq(AccessAuthTaskDO::getId, taskId);
        this.accessAuthTaskMapper.update(null, updateQuery);
    }
}
